package top.jpower.jpower.service.core.tenant.impl;

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.jpower.jpower.cache.param.ParamConfig;
import top.jpower.jpower.dbs.dao.core.dict.TbCoreDictDao;
import top.jpower.jpower.dbs.dao.core.org.TbCoreOrgDao;
import top.jpower.jpower.dbs.dao.core.role.TbCoreFunctionDao;
import top.jpower.jpower.dbs.dao.core.role.TbCoreRoleDao;
import top.jpower.jpower.dbs.dao.core.role.TbCoreRoleFunctionDao;
import top.jpower.jpower.dbs.dao.core.tenant.TbCoreTenantDao;
import top.jpower.jpower.dbs.dao.core.tenant.mapper.TbCoreTenantMapper;
import top.jpower.jpower.dbs.entity.core.dict.TbCoreDict;
import top.jpower.jpower.dbs.entity.core.function.TbCoreFunction;
import top.jpower.jpower.dbs.entity.core.org.TbCoreOrg;
import top.jpower.jpower.dbs.entity.core.role.TbCoreRole;
import top.jpower.jpower.dbs.entity.core.tenant.TbCoreTenant;
import top.jpower.jpower.dbs.entity.core.user.TbCoreUser;
import top.jpower.jpower.module.base.enums.JpowerError;
import top.jpower.jpower.module.base.exception.JpowerAssert;
import top.jpower.jpower.module.base.exception.JpowerException;
import top.jpower.jpower.module.common.service.impl.BaseServiceImpl;
import top.jpower.jpower.module.common.utils.DigestUtil;
import top.jpower.jpower.module.common.utils.Fc;
import top.jpower.jpower.module.common.utils.MD5;
import top.jpower.jpower.module.common.utils.ShieldUtil;
import top.jpower.jpower.module.common.utils.constants.ConstantsEnum;
import top.jpower.jpower.module.common.utils.constants.ConstantsUtils;
import top.jpower.jpower.module.common.utils.constants.ParamsConstants;
import top.jpower.jpower.module.mp.support.Condition;
import top.jpower.jpower.service.core.tenant.TenantService;
import top.jpower.jpower.service.core.user.CoreUserService;

import java.util.*;
import java.util.stream.Collectors;

import static top.jpower.jpower.module.common.utils.constants.JpowerConstants.TOP_CODE;
import static top.jpower.jpower.module.tenant.TenantConstant.DEFAULT_TENANT_CODE;
import static top.jpower.jpower.module.tenant.TenantConstant.TENANT_ACCOUNT_NUMBER;
import static top.jpower.jpower.module.tenant.TenantConstant.getLicenseKey;
import static top.jpower.jpower.module.tenant.TenantConstant.tenantCode;

/**
 * @ClassName TenantServiceImpl
 * @Description TODO 租户业务
 * @Author 郭丁志
 * @Date 2020-10-23 15:17
 * @Version 1.0
 */
@Service
@AllArgsConstructor
public class TenantServiceImpl extends BaseServiceImpl<TbCoreTenantMapper, TbCoreTenant> implements TenantService {

    private TbCoreTenantDao tenantDao;
    private TbCoreOrgDao orgDao;
    private TbCoreRoleDao roleDao;
    private TbCoreFunctionDao functionDao;
    private TbCoreRoleFunctionDao roleFunctionDao;
    private TbCoreDictDao dictDao;
    private CoreUserService userService;

    @Override
    public boolean updateById(TbCoreTenant tenant){
        tenant.setTenantCode(null);

        if (Fc.notNull(tenant.getExpireTime()) || Fc.notNull(tenant.getAccountNumber())){
            TbCoreTenant coreTenant = tenantDao.getById(tenant.getId());
            Date expireTime = Fc.isNull(tenant.getExpireTime())?coreTenant.getExpireTime():tenant.getExpireTime();
            Integer accountNumber = Fc.isNull(tenant.getAccountNumber())?coreTenant.getAccountNumber():tenant.getAccountNumber();
            tenant.setLicenseKey(getLicenseKey(accountNumber,expireTime));
        }

        return tenantDao.updateById(tenant);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class, JpowerException.class})
    public boolean save(TbCoreTenant tenant, Set<String> functionCodes){
        if (Fc.isBlank(tenant.getTenantCode())){
            List<String> tenantCodeList = tenantDao.listObjs(Condition.<TbCoreTenant>getQueryWrapper().lambda()
                    .select(TbCoreTenant::getTenantCode), Fc::toStr);
            tenant.setTenantCode(tenantCode(tenantCodeList));
        }
        if (Fc.isNull(tenant.getAccountNumber())){
            tenant.setAccountNumber(TENANT_ACCOUNT_NUMBER);
        }
        tenant.setLicenseKey(getLicenseKey(tenant.getAccountNumber(),tenant.getExpireTime()));
        if (tenantDao.save(tenant)){
            //创建租户默认部门
            TbCoreOrg org = new TbCoreOrg();
            org.setParentId(Fc.toLong(TOP_CODE));
            org.setName(tenant.getTenantName());
            org.setCode(tenant.getTenantCode());
            if (ShieldUtil.isRoot()){
                org.setTenantCode(tenant.getTenantCode());
            }
            org.setAncestorId(TOP_CODE);
            org.setSort(1);
            org.setContactName(tenant.getContactName());
            org.setContactPhone(tenant.getContactPhone());
            org.setAddress(tenant.getAddress());
            orgDao.save(org);
            //创建租户默认角色
            TbCoreRole role = new TbCoreRole();
            role.setIsSysRole(ConstantsEnum.YN01.Y.getValue());
            role.setName(tenant.getTenantName()+"-管理员");
            role.setParentId(Fc.toLong(TOP_CODE));
            if (ShieldUtil.isRoot()){
                role.setTenantCode(tenant.getTenantCode());
            }
            roleDao.save(role);
            //创建租户初始权限

            List<Long> functionIds = functionDao.queryIdByTopChild();

            if (Fc.isNotEmpty(functionCodes)){
                functionIds.addAll(getFunctions(functionCodes,new LinkedList<>()));
            }
            roleFunctionDao.saveFunctions(functionIds, role.getId());

            //创建租户默认字典
            List<TbCoreDict> dictList = dictDao.list(Condition.<TbCoreDict>getQueryWrapper().lambda().eq(TbCoreDict::getTenantCode,DEFAULT_TENANT_CODE).orderByAsc(TbCoreDict::getParentId));
            Map<Long,Long> map = new HashMap<>(dictList.size());
            dictList = dictList.stream().peek(dict->{
                dict.setTenantCode(tenant.getTenantCode());
                Long id = Fc.randomSnowFlakeId();
                //把旧ID和新ID的对应关系存储起来
                map.put(dict.getId(),id);
                dict.setId(id);
            }).collect(Collectors.toList());
            dictList = dictList.stream().peek(dict -> {
                if (!Fc.equalsValue(dict.getParentId(),TOP_CODE)){
                    dict.setParentId(map.get(dict.getParentId()));
                }
            }).collect(Collectors.toList());
            dictDao.addBatchSomeColumn(dictList);

            //创建租户默认用户 (必须放到最后创建，因为没有启动分布式事务)
            TbCoreUser user = new TbCoreUser();
            user.setLoginId("admin");
            user.setPassword(DigestUtil.pwdEncrypt(MD5.md5HexToUpperCase(ParamConfig.getString(ParamsConstants.USER_DEFAULT_PASSWORD, ConstantsUtils.DEFAULT_USER_PASSWORD))));
            user.setNickName(tenant.getTenantName()+"-管理员");
            user.setUserName(tenant.getTenantName()+"-管理员");
            user.setUserType(ConstantsEnum.USER_TYPE.USER_TYPE_SYSTEM.getValue());
            user.setBirthday(new Date());
            user.setActivationStatus(ConstantsEnum.YN01.Y.getValue());
            user.setOrgId(org.getId());
            user.setTenantCode(tenant.getTenantCode());

            JpowerAssert.isTrue(userService.saveUser(user,role.getId()), JpowerError.Business, "保存用户失败");
            return true;
        }
        return false;
    }

    private List<Long> getFunctions(Set<String> functionCodes,LinkedList<Long> functionIds) {

        List<Long> ids = functionDao.listObjs(Condition.<TbCoreFunction>getQueryWrapper().lambda()
                .select(TbCoreFunction::getId)
                .in(TbCoreFunction::getCode,functionCodes), Fc::toLong);

        ids.forEach(id->{
            functionIds.add(id);

            List<Long> btnIds = functionDao.listObjs(Condition.<TbCoreFunction>getQueryWrapper().lambda()
                    .select(TbCoreFunction::getId)
                    .ne(TbCoreFunction::getFunctionType,ConstantsEnum.FUNCTION_TYPE.MENU.getValue())
                    .eq(TbCoreFunction::getParentId,id), Fc::toLong);

            functionIds.addAll(btnIds);
        });

        return functionIds;
    }

    @Override
    public boolean setting(List<Long> ids, Integer accountNumber, Date expireTime) {
        String licenseKey = getLicenseKey(accountNumber,expireTime);
        List<TbCoreTenant> tenantList = new ArrayList<>();
        ids.forEach(id -> {
            TbCoreTenant tenant = new TbCoreTenant();
            tenant.setAccountNumber(accountNumber);
            tenant.setExpireTime(expireTime);
            tenant.setId(id);
            tenant.setLicenseKey(licenseKey);
            tenantList.add(tenant);
        });
        return tenantDao.updateBatchById(tenantList);
    }

    @Override
    public Map<String, String> config(Long id) {
        return tenantDao.config(id);
    }

    @Override
    public boolean updateConfig(Long id, Map<String, String> config) {
        return tenantDao.updateConfig(id, config);
    }

}
